/*
 * File     : DirectNativeInterface.java
 * Created  : 1 May 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dni;

import java.util.Map;

import com.googlecode.dni.internal.DniInternal;
import com.googlecode.dni.platform.PlatformGroup;
import com.googlecode.dni.platform.PlatformType;
import com.googlecode.dni.type.NativeString;
import com.googlecode.dni.type.Pointer;
import com.googlecode.dni.type.Region;
import com.googlecode.dni.type.StringPool;
import com.googlecode.dni.type.structure.IllegalStructureException;
import com.googlecode.dni.type.structure.Structure;
import com.googlecode.dni.type.structure.StructureFactory;
import com.googlecode.dni.type.structure.StructureInfo;


/**
 * <p>
 *  Provides the top-level entry point for Direct Native Interface.
 * </p>
 *
 * <!-- TODO Class documentation! -->
 *
 * @author Matthew Wilson
 */
public final class DirectNativeInterface
{

    private static final Map< InitialisationOption, Object > OPTIONS;

    static
    {
        // Do this once, and once only.
        System.loadLibrary( "dni" );

        // Pick up the initialisation options, if set.
        OPTIONS = InitialisationOption.Initialisation.getInitialisationOptions();

        DniInternal.LOGGER.info( "DNI initialised" );
    }

    /** Prevents instantiation. */
    private DirectNativeInterface()
    {
        throw new UnsupportedOperationException();
    }

    /**
     * <p>
     *  Creates an instance of a library API, given its type.
     * </p>
     * <p>
     *  The native library specified by the {@link Library} annotation is first
     *  loaded, then an implementation of the given interface is created that
     *  will call the method's equivalent native function.
     * </p>
     * <p>
     *  See the class documentation for details of how interface methods require
     *  annotation to specify the native function's details.
     * </p>
     *
     * @param <T>
     *            the type of interface
     * @param type
     *            the type of interface
     *
     * @return an implementation of the given interface
     *
     * @throws LinkageError
     *             if the library or libraries could not be loaded
     * @throws IllegalLibraryException
     *             if the given type is not an interface, or is not correctly
     *             annotated
     */
    public static < T > T getLibrary( final Class< T > type ) throws LinkageError
    {
        return DniInternal.getLibrary( type );
    }

    // TODO method passing in the various Library options

    /**
     * <p>
     *  Obtains an abstract factory that will create structures of the given
     *  type.
     * </p>
     *
     * @param <T>
     *            the type of structure
     * @param type
     *            the type of structure
     *
     * @return a factory for the structure
     *
     * @throws IllegalStructureException
     *             if the given type is not an interface, or is not correctly
     *             annotated
     */
    public static < T extends Structure > StructureFactory< T > getStructureFactory( final Class< T > type )
    {
        @SuppressWarnings( "unchecked" )
        StructureFactory< T > factory = (StructureFactory< T >) DniInternal.getStructureRecord( type ).getFactory();
        return factory;
    }

    /**
     * <p>
     *  Converts the given Java string into a native string.
     * </p>
     * <p>
     *  Temporary memory is allocated as required.  The string must be freed
     *  when no longer needed to release that memory.
     * </p>
     *
     * @param string
     *            the string
     *
     * @return a native string
     */
    public static NativeString wrapString( final String string )
    {
        throw new UnsupportedOperationException( "Implement me" );
    }

    /**
     * <p>
     *  Wraps the given null-terminated native string.
     * </p>
     * <p>
     *  The returned string cannot be freed; it is assumed that the caller is
     *  already tracking the native memory.
     * </p>
     * <p>
     *  No reading of memory is done at the point of this call.
     * </p>
     *
     * @param pointer
     *            the pointer to the first character of the string
     *
     * @return a native string
     */
    public static NativeString wrapString( final Pointer pointer )
    {
        throw new UnsupportedOperationException( "Implement me" );
    }

    /**
     * <p>
     *  Wraps the given null-terminated native string, with an upper bound on
     *  the size.
     * </p>
     * <p>
     *  The returned string cannot be freed; it is assumed that the caller is
     *  already tracking the native memory.
     * </p>
     * <p>
     *  No reading of memory is done at the point of this call.
     * </p>
     *
     * @param region
     *            the region of memory for the string
     *
     * @return a native string
     */
    public static NativeString wrapString( final Region region )
    {
        throw new UnsupportedOperationException( "Implement me" );
    }

    /**
     * <p>
     *  Creates a pool of strings.
     * </p>
     *
     * @param charset
     *            the charset to use; or {@link StringEncoding#DEFAULT_CHARSET}
     * @param chunkSize
     *            the initial size, and the size by which the pool will grow
     *            (taken as a hint)
     * @param maximumSize
     *            the maximum size of the pool (taken as a hint; may be rounded
     *            up slightly)
     *
     * @return a native string
     */
    public static StringPool createStringPool( final String charset,
                                               final int chunkSize,
                                               final int maximumSize )
    {
        throw new UnsupportedOperationException( "Implement me" );
    }

    /**
     * <p>
     *  Obtains information about the given structure.
     * </p>
     *
     * @param type
     *            the structure type
     *
     * @return structure information
     */
    public static StructureInfo getStructureInfo( final Class< ? extends Structure > type )
    {
        return DniInternal.getStructureRecord( type ).getInfo();
    }

    /**
     * <p>
     *  Determines the current platform group.
     * </p>
     *
     * @return the current platform group
     */
    public static PlatformGroup getPlatformGroup()
    {
        return DniInternal.PLATFORM.getPlatformGroup();
    }

    /**
     * <p>
     *  Determines the current platform type.
     * </p>
     *
     * @return the current platform type
     */
    public static PlatformType getPlatformType()
    {
        return DniInternal.PLATFORM.getPlatformType();
    }

    /**
     * <p>
     *  Obtains an allocator of native memory.
     * </p>
     *
     * @return an allocator
     */
    public static Allocator getAllocator()
    {
        return DniInternal.ALLOCATOR;
    }

    /**
     * <p>
     *  Obtains an option set during initialisation.
     * </p>
     *
     * @param option
     *            the option type
     *
     * @return the option's value
     */
    public static Object getOption( final InitialisationOption option )
    {
        return OPTIONS.get( option );
    }

    /**
     * Forces this class to be initialised.
     */
    static void init()
    {
        // nothing
    }

}
